#ifndef PITA_CONFIG_CONFIG_H_
#define PITA_CONFIG_CONFIG_H_

#include <map>
#include <string>

#include <boost/lexical_cast.hpp>

#include "exceptions.h"

namespace pita
{
	
	class Config
	{
		public:
			class RangeError : public Exception {};
			
			typedef std::multimap<std::string, std::string> Values;
			typedef std::multimap<std::string, Config>      Groups;
		
			class const_value_iterator;

			class value_iterator
			{
				public:
					typedef Config::Values::iterator iterator_type;
					typedef std::string deref_type;

					value_iterator(const iterator_type& itr) : itr_(itr) {}
				
					void operator++() { ++itr_; }
				
					/** Gets the iterator's value after attempting to convert it 
						to type T */
					template <class T>
					T as() const { return boost::lexical_cast<T>(itr_->second); }
				
					const std::string& key() const { return itr_->first; }
					operator std::string() const   { return itr_->second; }
					deref_type& operator*() const  { return itr_->second; }

					/** Assigns the value \val to the config */
					template <class T>
					void set(const T& val) 
						{ itr_->second = boost::lexical_cast<std::string>(val); }

					bool operator!=(const value_iterator& i) const { return (itr_ != i.itr_); }

				protected:
					iterator_type itr_;

					friend class const_value_iterator;
					friend class Config;
			};

			class const_value_iterator
			{
				public:
					typedef Config::Values::const_iterator iterator_type;
					typedef const std::string deref_type;

					const_value_iterator(const iterator_type& itr) : itr_(itr) {}
					const_value_iterator(const value_iterator& itr) : itr_ (itr.itr_) {}
				
					void operator++() { ++itr_; }
				
					/** Gets the iterator's value after attempting to convert it 
						to type T */
					template <class T>
					T as() const { return boost::lexical_cast<T>(itr_->second); }
				
					const std::string& key() const { return itr_->first; }
					operator std::string() const   { return itr_->second; }
					deref_type& operator*() const  { return itr_->second; }

					bool operator!=(const const_value_iterator& i) const { return (itr_ != i.itr_); }

				protected:
					iterator_type itr_;
					friend class Config;
			};

			template <typename ValueItr>
			class value_range_
			{
				public:
					typedef ValueItr value_iterator_type;
				
					value_range_(const value_iterator_type& begin,
								 const value_iterator_type& end) :
						begin_ (begin),
						end_ (end)
						{}

					const value_iterator_type& begin() const { return begin_; }
					const value_iterator_type& end() const { return end_; }

					template <typename T>
					T as() const { begin_.as<T>(); }

					const std::string& key() const { return begin_.key(); }
					operator std::string() const   { return begin_; }
					typename value_iterator_type::deref_type& operator*() const { return *begin_; }

					template <typename T>
					void set(const T& t) { begin_.set<T>(t); }

					bool empty() const { return !(begin_ != end_); }

				private:
					value_iterator_type begin_;
					value_iterator_type end_;
			};

			typedef value_range_<value_iterator> value_range;
			typedef value_range_<const_value_iterator> const_value_range;

			class const_group_iterator;

			// groups iterators
			class group_iterator
			{
				public:
					typedef Config::Groups::iterator iterator_type;
					typedef value_range value_range_type;

					group_iterator(const iterator_type& itr) : itr_(itr) {}
				
					void operator++() { ++itr_; }
				
					const std::string& key() const { return itr_->first; }
					Config& operator*() const { return itr_->second; }

					value_range operator[](const std::string& name) const { return itr_->second[name]; }

					void set(const Config& val) { itr_->second = val; }

					bool operator!=(const group_iterator& i) const { return (itr_ != i.itr_); }

				protected:
					iterator_type itr_;

					friend class const_group_iterator;
					friend class Config;
			};

			class const_group_iterator
			{
				public:
					typedef Config::Groups::const_iterator iterator_type;
					typedef const_value_range value_range_type;

					const_group_iterator(const iterator_type& itr) : itr_(itr) {}
					const_group_iterator(const group_iterator& itr) : itr_ (itr.itr_) {}
				
					void operator++() { ++itr_; }
				
					const std::string& key() const { return itr_->first; }
					const Config& operator*() const { return itr_->second; }

					const_value_range operator[](const std::string& name) const	{ return itr_->second[name]; }

					bool operator!=(const const_group_iterator& i) const { return (itr_ != i.itr_); }

				protected:
					iterator_type itr_;
					friend class Config;
			};

			template <typename GroupItr>
			class group_range_
			{
				public:
					typedef GroupItr                              group_iterator_type;
					typedef typename group_iterator_type::value_range_type value_range_type;

					group_range_(const group_iterator_type& begin,
								 const group_iterator_type& end) :
						begin_ (begin),
						end_ (end)
						{}

					const group_iterator_type& begin() const { return begin_; }
					const group_iterator_type& end() const { return end_; }

					const std::string& key() const { return begin_.key(); }
					value_range_type operator[](const std::string& name) const { return begin_[name]; }

					bool empty() const { return (begin_ == end_); }

				private:
					group_iterator_type begin_;
					group_iterator_type end_;
			};

			typedef group_range_<group_iterator> group_range;
			typedef group_range_<const_group_iterator> const_group_range;

			////////////////////////////////////////////////////////////////////////
			// Value-related functions
		
			const_value_range values() const { return const_value_range(values_.begin(),
																		values_.end()); }
			const_value_iterator valuesBegin() const { return values_.begin(); }
			const_value_iterator valuesEnd() const   { return values_.end(); }

			value_range values() { return value_range(values_.begin(),
													  values_.end()); }
			value_iterator valuesBegin() { return values_.begin(); }
			value_iterator valuesEnd()   { return values_.end(); }

			/** Returns the range of iterators for values at \path */
			const_value_range operator[](const std::string& name) const
				{
					std::pair<Values::const_iterator, Values::const_iterator> rng =
						values_.equal_range(name);
					return const_value_range(rng.first, rng.second);
				}
			value_range operator[](const std::string& name)
				{
					std::pair<Values::iterator, Values::iterator> rng =
						values_.equal_range(name);
					return value_range(rng.first, rng.second);
				}

			template <typename T>
			value_iterator insert(const std::string& name, const T& val)
				{
					return value_iterator(
						values_.insert(
							make_pair(
								name, 
								boost::lexical_cast<std::string>(val))));
				}

			void clear(const std::string& name) { values_.erase(name); }
			void erase(const value_iterator itr) { values_.erase(itr.itr_); }
		
			////////////////////////////////////////////////////////////////////////
			// Group-related functions

			const_group_range groups() const { return const_group_range(groups_.begin(),
																		groups_.end()); }
			const_group_iterator groupsBegin() const { return groups_.begin(); }
			const_group_iterator groupsEnd() const { return groups_.end(); }

			group_range groups() { return group_range(groups_.begin(),
													  groups_.end()); }
			group_iterator groupsBegin() { return groups_.begin(); }
			group_iterator groupsEnd() { return groups_.end(); }

			const_group_range operator()(const std::string& name) const 
				{ 
					std::pair<Groups::const_iterator, Groups::const_iterator> rng =
						groups_.equal_range(name);
					return const_group_range(rng.first, rng.second);
				}

			group_range operator()(const std::string& name)
				{
					std::pair<Groups::iterator, Groups::iterator> rng =
						groups_.equal_range(name);
					return group_range(rng.first, rng.second);
				}

			group_iterator insertGroup(const std::string& name) { return groups_.insert(std::make_pair(name, Config())); }
			void clearGroups(const std::string& name) { groups_.erase(name); }
			void eraseGroup(const group_iterator& itr) { groups_.erase(itr.itr_); }

		private:
			Values values_;
			Groups groups_;
	};

}

#endif
